import { ArrayMethods } from "./arr";

export function observe(data) {
    // console.log("劫持数据：", data);

    // 数据可能为空，不需要劫持
    if (typeof data != 'object' || data == null) {
        return data
    }
    // 对象的形式，通过一个泪来劫持
    // 劫持对象 {name: 'sa',age: 20}
    // 劫持数组 {userList: []}
    return new observer(data)
}
class observer {
    constructor(value) {
        // ----------------------------------------------------------------------------------
        // 给每个属性值绑定一个this对象，因为this指向observer的实例，
        // arr.js中可以通过属性值获取observer的实例，从而调用observeArray方法实现劫持
        Object.defineProperty(value,"__ob__",{
            enumerable:false,
            value:this
        })
        // -------------------------------------------------------------
        // 判断是对象还是数组，因为observe方法在多个地方被调用，这个构造函数也是多次执行
        // 所以value的值的类型是不确定的
        if (Array.isArray(value)) {
            // console.log("劫持数组", value);
            // 给数组的原型挂在我们重写后的数组方法
            value.__proto__ = ArrayMethods
            // -------------------------------------------------------------------------
            // 处理数组对象
            this.observeArray(value)
        } else {
            // console.log("劫持对象");
            // 遍历对象中所有的属性
            this.walk(value)
        }
        // ------------------------------------------------------------
    }
    walk(value) {
        let keys = Object.keys(value)
        // console.log(keys);
        for (let i = 0; i < keys.length; i++) {
            // 对每个属性执行object.defineProperty，劫持属性（监听获取和修改行为）
            let k = keys[i]
            // 取出属性的值
            let v = value[k]

            defineReactive(value, k, v)
        }
    }
    // ------------------------------------------------------------
    observeArray(value){
        // 对象数组，就是每个元素还是对象，直接调用之前写好的对象劫持逻辑即可
        for(let i=0;i<value.length;i++){
            observe(value[i])
        }
    }
    // ---------------------------------------------------------------------------
}
function defineReactive(value, k, v) {
    // -----------------------------------------------------
    // 深度递归，只要是对象嵌套对象，任意层都可以劫持到属性值的获取和修改操作
    observe(v)
    // ----------------------------------------------------------

    // Object是js常用类，想想用过哪些方法？如下是固定写法
    // Object.defineProperty（目标对象，目标属性，劫持策略）
    Object.defineProperty(value, k, {
        get() {
            // console.log("获取");
            return v
        },
        set(newValue) {
            // console.log(k, "修改成新值", newValue);
            // -------------------------------------------------------------
            // 用新的值覆盖旧的值，也要保证有劫持的效果
            observe(newValue)
            // ------------------------------------------------------------------------
            v = newValue
        }
    })
}
// Object.defineProperty 执行一次只能对一个属性进行劫持